package com.invengo.module_check.ui.fragment.bookdown.fragment;

import android.annotation.SuppressLint;
import android.app.Application;
import android.os.Build;
import android.os.Handler;
import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.databinding.ObservableArrayList;
import androidx.databinding.ObservableBoolean;
import androidx.databinding.ObservableField;
import androidx.databinding.ObservableList;

import com.invengo.base.utils.BeanMapperUtils;
import com.invengo.library_db.entity.module_check.BookBean;
import com.invengo.library_db.entity.module_check.ShelfBean;
import com.invengo.library_rfid.classify.AnalyzeImpl;
import com.invengo.library_rfid.classify.RFIDCallBackListener;
import com.invengo.library_rfid.entity.TagInfo;
import com.invengo.module_check.BR;
import com.invengo.module_check.R;
import com.invengo.module_check.data.repsitory.business.BusinessRepsitory;
import com.invengo.module_check.ui.fragment.bookdown.fragment.item.BookDownByBookItemViewModel;
import com.invengo.module_check.ui.fragment.netmodel.utils.DownloadSubscriber;

import org.reactivestreams.Subscription;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import io.reactivex.Observable;
import java8.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import io.reactivex.Flowable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import java8.util.stream.RefStreams;
import java8.util.stream.StreamSupport;
import me.goldze.mvvmhabit.base.BaseViewModel;
import me.goldze.mvvmhabit.base.ItemViewModel;
import me.goldze.mvvmhabit.binding.command.BindingCommand;
import me.goldze.mvvmhabit.bus.event.SingleLiveEvent;
import me.goldze.mvvmhabit.http.UpdateResult;
import me.goldze.mvvmhabit.utils.KLog;
import me.goldze.mvvmhabit.utils.RxUtils;
import me.goldze.mvvmhabit.utils.StringUtils;
import me.goldze.mvvmhabit.utils.ToastUtils;
import me.goldze.mvvmhabit.utils.cache.ACache;
import me.goldze.mvvmhabit.utils.cache.AKey;
import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapter;
import me.tatarka.bindingcollectionadapter2.ItemBinding;

public class BookDownViewModel extends BaseViewModel<BusinessRepsitory> {
    public static final String DOWN_BY_BOOK = "BookDownByBookFragment";
    public static final String DOWN_BY_MULTIPLE_SHELF = "BookDownByMultipleShelfFragment";
    public static final String DOWN_BY_SHELF = "BookDownBySingleShelfFragment";

    public ObservableBoolean openSelectShelf = new ObservableBoolean(ACache.getInstance().getAsBoolean(AKey.IS_OPEN_SELECT_SHELF));//是否开启层架选择


    public RFIDCallBackListener rfidService = new RFIDCallBackListener(AnalyzeImpl.TO_BOOK, tagInfo -> onScanResult(tagInfo), state -> isReading.set(state));
    public BindingRecyclerViewAdapter<BookDownByBookItemViewModel> adapter = new BindingRecyclerViewAdapter<>();
    //给RecyclerView添加ObservableList
    //    =====================这部分是根据图书=========================
    public ObservableList<BookDownByBookItemViewModel> byBookItemList = new ObservableArrayList<>();
    public ItemBinding<BookDownByBookItemViewModel> byBookitemBinding = ItemBinding.of(BR.viewModel, R.layout.item_book_down_by_book);

    //    =====================这部分是根据层架=========================
    public ObservableField<String> shelfName = new ObservableField<>();
    public ObservableField<String> shelfCode = new ObservableField<>();

    public ObservableList<BookDownByBookItemViewModel> byShelfItemList = new ObservableArrayList<>();

    public ItemBinding<BookDownByBookItemViewModel> byShelfitemBinding = ItemBinding.of(BR.viewModel, R.layout.item_book_down_by_book);
    //    =====================这部分是根据多层=========================
    public ObservableBoolean isFirst = new ObservableBoolean(true);
    public ObservableField<Integer> shelfCount = new ObservableField(0);
    public ObservableField<String> firstShelfCode = new ObservableField<>();
    public ObservableField<String> firstShelfName = new ObservableField<>();

    public ObservableField<String> secondShelfName = new ObservableField<>();
    public ObservableField<String> secondShelfCode = new ObservableField<>();

    public ObservableList<BookDownByBookItemViewModel> byMultiItemList = new ObservableArrayList<>();
    public ItemBinding<BookDownByBookItemViewModel> byMultiItemBinding = ItemBinding.of(BR.viewModel, R.layout.item_book_down_by_book);

    public BookDownViewModel(@NonNull Application application) {
        super(application, BusinessRepsitory.getInstance());
        registerRFID(rfidService);
    }


    public void onScanResult(TagInfo tagInfo) {
        switch (tagInfo.getRfidModel()) {
            case TID:
                if (tagInfo.getScanModel() == AnalyzeImpl.TO_SHELF)
                    getShelfByTid(tagInfo.getTag());
                else
                    getBookByTid(tagInfo.getTag());
                break;
            case EPC:
            case U_DATA:
                if (tagInfo.getScanModel() == AnalyzeImpl.TO_SHELF)
                    getShelfByCode(tagInfo.getTag());
                else
                    getBookByCode(tagInfo.getTag());
                break;


        }
    }

    /**
     * 按书架查询书籍
     *
     * @param shelfCode 标签
     */

    private void getBookByShelfCode(String shelfCode) {
        model.getBookByShelfCode(shelfCode)
                .compose(RxUtils.applySchedulersToast(getLifecycleProvider(), this))
                .flatMap(bookBeans -> Observable.fromIterable(bookBeans))
                .toList()//李为民有个项目导致的问题
//                .toSortedList((o1,o2)->o1.getPretendCallNo().compareTo(o2.getPretendCallNo()))
                .subscribe(bookBeans -> {
                    for (BookBean bean : bookBeans) {
                        addItem(bean);
                    };
                });
    }

    /**
     * 按tid取架
     *
     * @param tid 时间
     */
    private void getShelfByTid(String tid) {
        model.getShelfInfoByTid(tid)
                .compose(RxUtils.applySchedulersIO(getLifecycleProvider(), this))
                .subscribe(bean -> {
                    handlerShelf(bean);
                });
    }


    /**
     * 查询货架信息
     *
     * @param shelfCode 标签
     */
    private void getShelfByCode(String shelfCode) {
        model.getShelfInfoByCode(shelfCode)
                .compose(RxUtils.applySchedulersIO(getLifecycleProvider(), this))
                .subscribe(bean -> {
                    handlerShelf(bean);
                });
    }

    public void handlerShelf(ShelfBean bean) {
        getUC().getHideKeyboardEvent().call();
        switch (getCacheKey()) {
            case DOWN_BY_SHELF:
                byShelfItemList.clear();
                shelfName.set(bean.getShelfName());
                shelfCode.set(bean.getShelfCode());
                getBookByShelfCode(bean.getShelfCode());
                break;
            case DOWN_BY_MULTIPLE_SHELF:
                if (isFirst.get()) {
                    firstShelfCode.set(bean.getShelfCode());
                    firstShelfName.set(bean.getShelfName());
                    changeForce(false);
                } else {
                    secondShelfCode.set(bean.getShelfCode());
                    secondShelfName.set(bean.getShelfName());
                }
                if (!StringUtils.isEmpty(firstShelfCode.get()) && !StringUtils.isEmpty(secondShelfCode.get())) {
                    if (TextUtils.isEmpty(firstShelfCode.get()) | TextUtils.isEmpty(secondShelfCode.get())) {
                        ToastUtils.showShort("请先扫描层位标签或输入层位代码");
                        return;
                    }
                    shelfCount.set(0);
                    byMultiItemList.clear();
                    int compareResult = firstShelfCode.get().compareTo(secondShelfCode.get());
                    if (compareResult >= 0) {
                        ToastUtils.showShort("起始层位不能大于或等于截止层位");
                        secondShelfCode.set("");
                        secondShelfName.set("");
                        return;
                    }
                    getBookByMultiShelf();
                }
                break;
        }
    }

    /**
     * 按Tid查询书本
     *
     * @param tid 标签
     */
    private void getBookByTid(String tid) {
        model.getBookByTid(tid)
                .compose(RxUtils.applySchedulersToast(getLifecycleProvider(), this))
                .subscribe(bookBean -> {
                    addItem(bookBean);
                });
    }

    /**
     * 按Tid查询书本
     *
     * @param code 标签
     */
    private void getBookByCode(String code) {
        model.getBookByCode(code)
                .compose(RxUtils.applySchedulersToast(getLifecycleProvider(), this))
                .subscribe(bookBean -> {
                    addItem(bookBean);
                });
    }

    private void getBookByMultiShelf() {
        stopScan();
        model.getBookByMultiShelf(firstShelfCode.get(), secondShelfCode.get(), "5000")
                .timeout(5 * 6000, TimeUnit.MILLISECONDS)
                .compose(RxUtils.applySchedulersToast(getLifecycleProvider(), this))
                .subscribe(bookBeans -> {
                    cleanRFIDCache();
                    shelfCount.set(bookBeans.size());
                    for (BookBean bean : bookBeans) {
                        addItem(bean);
                    }
                }, throwable -> {
                    cleanSecondShelf();
                });
    }

    private void cleanSecondShelf() {
        secondShelfCode.set("");
        secondShelfName.set("");
        isFirst.set(false);
    }

    List<List<BookBean>> downLoadBeans;
    private Subscription downFlowable;
    private static int downSize = 100;

    public ObservableField<Object[]> args = new ObservableField<>(new Object[5]);
    private String downBookinfo = "%d本待下架图书.. \n每份 %d 本 ，共%s份";

    private double minProgress = 0;

    /**
     * 更新对话框
     *
     * @param content  所容纳之物
     * @param progress 进步
     */
    private void updateDialog(String content, double progress) {
        uiChage.downloadContentEvent.postValue(content);
        minProgress += progress;
        if (minProgress > 1) {
            double floor = Math.floor(minProgress);
            minProgress -= floor;
            uiChage.downloadProgressEvent.postValue((int) floor);
        }
    }

    private static Integer countStep(Integer size) {
        return (size + downSize - 1) / downSize;
    }

    private float pencent = 0;
    private int downIndex = 0;

    private void downBook(List<BookBean> bookBeans) {
        if (bookBeans == null | bookBeans.size() == 0) {
            ToastUtils.showShort("列表中暂无可下架图书。。。");
            return;
        }
        uiChage.downloadDialogEvent.postValue(true);
        failBooks = new ArrayList<>();
        successBooks = new ArrayList<>();
        int limit = countStep(bookBeans.size());
        downIndex=0;
        downLoadBeans = RefStreams
                .iterate(0, f -> f + 1)
                .limit(limit)
                .map(a -> StreamSupport.stream(bookBeans).skip(a * downSize).limit(downSize).collect(java8.util.stream.Collectors.toList()))
                .collect(java8.util.stream.Collectors.toList());
        args.get()[0] = bookBeans.size();
        args.get()[1] = downSize;
        args.get()[2] = downLoadBeans.size();
        pencent = 100f / downLoadBeans.size();
        Flowable.fromIterable(downLoadBeans)
                .subscribeOn(Schedulers.io())
                .observeOn(Schedulers.io())
                .doOnSubscribe(s -> downFlowable = s)
                .subscribe(new DownloadSubscriber<List<BookBean>>() {
                    @Override
                    public void onNext(List<BookBean> beans) {
                        updateDialog(String.format(downBookinfo, args.get()), pencent * 0.5);
                        downloadByLimit(beans);
                    }
                });
    }

    private List<BookBean> failBooks =new ArrayList<>();
    private List<BookBean> successBooks =new ArrayList<>();
    public List<BookBean> getFialBooks() {
        return failBooks;
    }
    private void downloadByLimit(List<BookBean> bookBeans) {
        model.downBook(BeanMapperUtils.cloneList(bookBeans))
                .compose(RxUtils.applySchedulersIO(getLifecycleProvider(), this))
                .subscribe(updateResult -> {
                    updateDialog(String.format(downBookinfo, args.get()), pencent * 0.5);
                    if (updateResult.getAffactNum()==bookBeans.size()){
                        downIndex++;
                        successBooks.addAll(bookBeans);
                        if (downIndex == (Integer) args.get()[2]) {
                            cleanRFIDCache(DOWN_BY_BOOK);
                            cleanRFIDCache(DOWN_BY_SHELF);
                            cleanRFIDCache(DOWN_BY_MULTIPLE_SHELF);
                            uiChage.downloadDialogEvent.postValue(false);
                            uiChage.nextEvent.postValue(successBooks);
                            cleanAllData.execute();
                        } else {
                            new Handler().postDelayed(() -> downFlowable.request(1), 500);
                        }
                    }else{
                        failBooks.addAll(bookBeans);
                        if (downIndex == (Integer) args.get()[2]) {
                            uiChage.downloadDialogEvent.postValue(false);
                            uiChage.nextEvent.postValue(successBooks);
                            cleanAllData.execute();
                        }else{
                            new Handler().postDelayed(() -> downFlowable.request(1), 500);
                        }
                    }
                }, throwable -> {
                    downIndex++;
                    failBooks.addAll(bookBeans);
                    if (downIndex == (Integer) args.get()[2]) {
                        uiChage.downloadDialogEvent.postValue(false);
                        uiChage.nextEvent.postValue(successBooks);
                        cleanAllData.execute();
                    }else{
                        new Handler().postDelayed(() -> downFlowable.request(1), 500);
                    }
                });
    }




    /**
     * 添加项目
     *
     * @param bean 豆
     */

    private void addItem(BookBean bean) {
        Optional<BookBean> any;
        switch (getCacheKey()) {
            case DOWN_BY_BOOK:
                byBookItemList.add(new BookDownByBookItemViewModel(this, bean));
                break;
            case DOWN_BY_SHELF:
                byShelfItemList.add(new BookDownByBookItemViewModel(this, bean));
                break;
            case DOWN_BY_MULTIPLE_SHELF:
                byMultiItemList.add(new BookDownByBookItemViewModel(this, bean));
                break;
        }
    }

    public UIChangeObservable uiChage = new UIChangeObservable();



    public class UIChangeObservable {
        public SingleLiveEvent<Boolean> multiChangeEvent = new SingleLiveEvent<>();
        public SingleLiveEvent<List<BookBean>> nextEvent = new SingleLiveEvent<>();
        public SingleLiveEvent<ShelfBean> selectShelfEvent = new SingleLiveEvent<>();
        public SingleLiveEvent<String> downloadContentEvent = new SingleLiveEvent<>();
        public SingleLiveEvent<Integer> downloadProgressEvent = new SingleLiveEvent<>();
        public SingleLiveEvent<Boolean> downloadDialogEvent = new SingleLiveEvent<>();
    }

    public void onItemRemove(ItemViewModel bean) {
        int position = -1;
        switch (getCacheKey()) {
            case DOWN_BY_BOOK:
                position = byBookItemList.indexOf(bean);
                byBookItemList.remove(position);
                break;
            case DOWN_BY_SHELF:
                position = byShelfItemList.indexOf(bean);
                byShelfItemList.remove(position);
                break;
            case DOWN_BY_MULTIPLE_SHELF:
                position = byMultiItemList.indexOf(bean);
                byMultiItemList.remove(position);
                break;
        }
        adapter.notifyDataSetChanged();
        ToastUtils.showShort("删除成功");
    }

    /**
     * 获取position的方式有很多种,indexOf是其中一种，常见的还有在Adapter中、ItemBinding.of回调里
     *
     * @return
     */
    public int getPosition(BookDownByBookItemViewModel item) {
        switch (getCacheKey()) {
            case DOWN_BY_BOOK:
                return byBookItemList.indexOf(item);
            case DOWN_BY_SHELF:
                return byShelfItemList.indexOf(item);
            case DOWN_BY_MULTIPLE_SHELF:
                return byMultiItemList.indexOf(item);
        }
        return -1;
    }


    /**
     * 清除所有数据
     */
    public BindingCommand cleanAllData = new BindingCommand(() -> {
        cleanRFIDCache();
        switch (getCacheKey()) {
            case DOWN_BY_BOOK:
                byBookItemList.clear();
                break;
            case DOWN_BY_SHELF:
                byShelfItemList.clear();
                shelfName.set("");
                shelfCode.set("");
                break;
            case DOWN_BY_MULTIPLE_SHELF:
                firstShelfName.set("");
                firstShelfCode.set("");
                secondShelfName.set("");
                secondShelfCode.set("");
                byMultiItemList.clear();
                uiChage.multiChangeEvent.postValue(true);
                break;
        }
    });

    public BindingCommand<String> textChangeCommand = new BindingCommand<>(s -> {
        getUC().getHideKeyboardEvent().call();
        getShelfByCode(s);
    });


    public BindingCommand move2First = new BindingCommand<>(() -> {
        KLog.e("移动第11111");
        isFirst.set(true);
    });
    public BindingCommand move2Secound = new BindingCommand<>(() -> {
        KLog.e("移动第二222");
        isFirst.set(false);
    });

    public void changeForce(Boolean b) {
        uiChage.multiChangeEvent.postValue(b);
    }

    public BindingCommand<String> byShelfChange = new BindingCommand<>(s -> {
        KLog.e("执行操作。。。。。" + isFirst.get());
        getShelfByCode(s);
    });


    /**
     * 按书本下架
     */
    public BindingCommand downByBookCommand = new BindingCommand(() -> {
        downBook(getMatchBooks());
    });

    private List<BookBean> getMatchBooks() {
        ObservableList<BookDownByBookItemViewModel> itemList = new ObservableArrayList<>();
        switch (getCacheKey()) {
            case DOWN_BY_BOOK:
                itemList = byBookItemList;
                break;
            case DOWN_BY_SHELF:
                itemList = byShelfItemList;
                break;
            case DOWN_BY_MULTIPLE_SHELF:
                itemList = byMultiItemList;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return itemList.stream()
                    .map(viewModel -> viewModel.book.getValue())
                    .filter(bean -> !TextUtils.isEmpty(bean.getShelfCode()))
                    .collect(Collectors.toList());
        }else{
            return StreamSupport.stream(itemList)
                    .map(viewModel -> viewModel.book.getValue())
                    .filter(bean -> !TextUtils.isEmpty(bean.getShelfCode()))
                    .collect(java8.util.stream.Collectors.toList());
        }
    }

    public void shelectShelf(boolean isFirst) {
        ShelfBean bean = null;
        switch (getCacheKey()) {
            case DOWN_BY_SHELF:
                if (!TextUtils.isEmpty(shelfName.get()) && !TextUtils.isEmpty(shelfCode.get())) {
                    bean = new ShelfBean();
                    bean.setShelfName(shelfName.get());
                    bean.setShelfCode(shelfCode.get());
                }
                uiChage.selectShelfEvent.postValue(bean);
                break;
            case DOWN_BY_MULTIPLE_SHELF:
                String name = isFirst ? firstShelfName.get() : secondShelfName.get();
                String code = isFirst ? firstShelfCode.get() : secondShelfCode.get();
                if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(code)) {
                    bean = new ShelfBean();
                    bean.setShelfName(name);
                    bean.setShelfCode(code);
                }
                uiChage.multiChangeEvent.postValue(isFirst);
                uiChage.selectShelfEvent.postValue(bean);
                break;
        }
    }
}
